`include "CacheDefines.svh"
`include "defines.svh"
`define RstEnable 1'b0
typedef struct packed {
    logic [ 3: 0] arid;
    logic [31: 0] araddr;
    logic [ 3: 0] arlen;
    logic [ 2: 0] arsize;
    logic [ 1: 0] arburst;
    logic [ 1: 0] arlock;
    logic [ 3: 0] arcache;
    logic [ 2: 0] arprot;
    logic         arvalid;
    logic         arready;
    logic [ 3: 0] rid;
    logic [31: 0] rdata;
    logic [ 1: 0] rresp;
    logic         rlast;
    logic         rvalid;
    logic         rready;
    logic [ 3: 0] awid;
    logic [31: 0] awaddr;
    logic [ 3: 0] awlen;
    logic [ 2: 0] awsize;
    logic [ 1: 0] awburst;
    logic [ 1: 0] awlock;
    logic [ 3: 0] awcache;
    logic [ 2: 0] awprot;
    logic         awvalid;
    logic         awready;
    logic [ 3: 0] wid;
    logic [31: 0] wdata;
    logic [ 3: 0] wstrb;
    logic         wlast;
    logic         wvalid;
    logic         wready;
    logic [ 3: 0] bid;
    logic [ 1: 0] bresp;
    logic         bvalid;
    logic         bready;
} AXISignal;

typedef enum logic [3:0] {
    I_RD_EMPTY ,
    I_RD_RECREQ,
    I_RD_WAIT1 ,
    I_RD_WAIT2 ,
    I_RD_WAIT3 ,
    I_RD_WAIT4 ,
    I_RD_FINISH
} ICacheRD;

typedef enum logic [3:0] {
    D_RD_EMPTY, 
    D_RD_RECREQ,
    D_RD_WAIT1, 
    D_RD_WAIT2, 
    D_RD_WAIT3, 
    D_RD_WAIT4, 
    D_RD_FINISH
} DCacheRD;

typedef enum logic [3:0] {
    D_WR_EMPTY, 
    D_WR_RECREQ,
    D_WR_WAIT1, 
    D_WR_WAIT2, 
    D_WR_WAIT3, 
    D_WR_WAIT4, 
    D_WR_S,     
    D_WR_FINISH
} DCacheWR;

typedef enum logic [2:0] {
    U_RD_EMPTY, 
    U_RD_RECREQ,
    U_RD_WAIT, 
    U_RD_FINISH
} UnCacheRD;

typedef enum logic [3:0] {
    U_WR_EMPTY, 
    U_WR_RECREQ,
    U_WR_WAIT, 
    U_WR_S,     
    U_WR_FINISH
} UnCacheWR;

module AXI (
    // Basic Clock
    input logic clk,
    input logic rst,
    // Cache Bus
    AXI_IBus_Interface      ICache_axi_interface,
    AXI_DBus_Interface      DCache_axi_interface,
    AXI_UICache_Interface   UICache_axi_interface,
    AXI_UDCache_Interface   UDCache_axi_interface,
    // AXI Signals
    // Read address request 
    output logic [ 3: 0] m_axi_arid,
    output logic [31: 0] m_axi_araddr,
    output logic [ 3: 0] m_axi_arlen,
    output logic [ 2: 0] m_axi_arsize,
    output logic [ 1: 0] m_axi_arburst,
    output logic [ 1: 0] m_axi_arlock,
    output logic [ 3: 0] m_axi_arcache,
    output logic [ 2: 0] m_axi_arprot,
    output logic         m_axi_arvalid,
    input  logic         m_axi_arready,
    // Read response
    input  logic [ 3: 0] m_axi_rid, 
    input  logic [31: 0] m_axi_rdata,
    input  logic [ 1: 0] m_axi_rresp,
    input  logic         m_axi_rlast,
    input  logic         m_axi_rvalid,
    output logic         m_axi_rready,
    // Write address request
    output logic [ 3: 0] m_axi_awid,
    output logic [31: 0] m_axi_awaddr,
    output logic [ 3: 0] m_axi_awlen,
    output logic [ 2: 0] m_axi_awsize,
    output logic [ 1: 0] m_axi_awburst,
    output logic [ 1: 0] m_axi_awlock,
    output logic [ 3: 0] m_axi_awcache,
    output logic [ 2: 0] m_axi_awprot,
    output logic         m_axi_awvalid,
    input  logic         m_axi_awready,
    // Write data request
    output logic [ 3: 0] m_axi_wid,
    output logic [31: 0] m_axi_wdata,
    output logic [ 3: 0] m_axi_wstrb,
    output logic         m_axi_wlast,
    output logic         m_axi_wvalid,
    input  logic         m_axi_wready,
    // Write response
    input  logic [ 3: 0] m_axi_bid,
    input  logic [ 1: 0] m_axi_bresp,
    input  logic         m_axi_bvalid,
    output logic         m_axi_bready
);
// We have to list all these signals as io ports
// in order to connect them with the mycpu_top's testbench port

    AXISignal I_bus;
    AXISignal D_bus;
    AXISignal UI_bus;
    AXISignal UD_bus;

    ICacheRD I_rd_state;
    ICacheRD I_rd_next_state;

    DCacheRD D_rd_state;
    DCacheRD D_rd_next_state;
    DCacheWR D_wr_state;
    DCacheWR D_wr_next_state;

    UnCacheRD UI_rd_state;
    UnCacheRD UI_rd_next_state;

    UnCacheRD UD_rd_state;
    UnCacheRD UD_rd_next_state;
    UnCacheWR UD_wr_state;
    UnCacheWR UD_wr_next_state;

    logic [2:0]     I_rd_data_selector;
    logic [31:0]    I_rd_addr;
    logic [127:0]   I_rd_data; // data read from axi ram

    logic [2:0]     D_rd_data_selector;
    logic [31:0]    D_rd_addr;
    logic [127:0]   D_rd_data;
    logic [31:0]    D_wr_addr;
    logic [127:0]   D_wr_data;

    logic [31:0]    UI_rd_addr;
    logic [31:0]    UI_rd_data;

    logic [31:0]    UD_rd_addr;
    logic [31:0]    UD_rd_data;
    logic [31:0]    UD_wr_addr;
    logic [31:0]    UD_wr_data;
    logic [3:0]     UD_wr_wstrb;

/*----------------------------------IBUS------------------------------------*/
    assign I_bus.arid    = 4'b0000;  // inst fetch id 0
    assign I_bus.arlen   = 4'b0011;  // 4 words per burst
    assign I_bus.arsize  = 3'b010;   // 4 bytes(1 word) a pack; each time a line = 4 by 4 bytes
    assign I_bus.arburst = 2'b01;    // increase mode
    assign I_bus.arcache = '0;
    assign I_bus.arprot  = '0;

    assign I_bus.awid    = '0; // God knows whether this's necessary
    assign I_bus.awlen   = '0;
    assign I_bus.awaddr  = '0;
    assign I_bus.awsize  = '0;
    assign I_bus.awburst = '0;
    assign I_bus.awlock  = '0;
    assign I_bus.awcache = '0; // maybe no influence
    assign I_bus.awprot  = '0;
    assign I_bus.awvalid = '0;

    assign I_bus.wid     = '0;
    assign I_bus.wdata   = '0;
    assign I_bus.wstrb   = '0;
    assign I_bus.wlast   = '0;
    assign I_bus.wvalid  = '0;

    assign I_bus.bready  = '0;

    assign ICache_axi_interface.rd_rdy  = (I_rd_state == I_RD_EMPTY) ? 1'b1 : 1'b0;

    // Buffer Request address
    always_ff @(posedge clk) begin
        if (rst == `RstEnable) begin
            I_rd_addr <= '0;
        end 
        else if ( ICache_axi_interface.rd_req == 1'b1 && I_rd_state == I_RD_EMPTY ) begin
            I_rd_addr <= ICache_axi_interface.rd_addr;
        end
    end
    // FSM Sequential Logic
    always_ff @( posedge clk ) begin
        if (rst == `RstEnable) begin
            I_rd_state <= I_RD_EMPTY;
        end else begin
            I_rd_state <= I_rd_next_state;
        end
    end

    // State transition & Data transfer select
    always_comb begin
        unique case (I_rd_state)
            I_RD_EMPTY: begin
                I_rd_data_selector = '0;
                if (ICache_axi_interface.rd_req == 1'b1) begin
                    I_rd_next_state = I_RD_RECREQ;
                end else begin
                    I_rd_next_state = I_RD_EMPTY;
                end
            end
            I_RD_RECREQ: begin
                I_rd_data_selector = '0;
                if (I_bus.arvalid == 1'b1 && I_bus.arready == 1'b1) begin
                    I_rd_next_state = I_RD_WAIT1;
                end else begin
                    I_rd_next_state = I_RD_RECREQ;
                end
            end
            I_RD_WAIT1: begin
                if (I_bus.rvalid == 1'b1 && I_bus.rready == 1'b1) begin
                    I_rd_next_state = I_RD_WAIT2;
                    I_rd_data_selector = 3'd1;
                end else begin
                    I_rd_next_state = I_RD_WAIT1;
                    I_rd_data_selector = 3'd0;
                end
            end
            I_RD_WAIT2: begin
                if (I_bus.rvalid == 1'b1 && I_bus.rready == 1'b1) begin
                    I_rd_next_state = I_RD_WAIT3;
                    I_rd_data_selector = 3'd2;
                end else begin
                    I_rd_next_state = I_RD_WAIT2;
                    I_rd_data_selector = 3'd0;
                end
            end
            I_RD_WAIT3: begin
                if (I_bus.rvalid == 1'b1 && I_bus.rready == 1'b1) begin
                    I_rd_next_state = I_RD_WAIT4;
                    I_rd_data_selector = 3'd3;
                end else begin
                    I_rd_next_state = I_RD_WAIT3;
                    I_rd_data_selector = 3'd0;
                end
            end
            I_RD_WAIT4: begin
                if (I_bus.rvalid == 1'b1 && I_bus.rready && I_bus.rlast == 1'b1) begin
                    I_rd_next_state = I_RD_FINISH;
                    I_rd_data_selector = 3'd4;
                end else begin
                    I_rd_next_state = I_RD_WAIT4;
                    I_rd_data_selector = 3'd0;
                end
            end
            I_RD_FINISH: begin
                I_rd_next_state = I_RD_EMPTY;
                I_rd_data_selector = '0;
            end
            default: begin
                I_rd_next_state = I_RD_EMPTY;
                I_rd_data_selector = '0;
            end
        endcase
    end
    // fill data into cache by clk
    always_ff @ (posedge clk) begin
        if (rst == `RstEnable) begin
            I_rd_data <= '0;
        end else begin
            unique case (I_rd_data_selector)
                3'd1: I_rd_data[ 31:  0] <= I_bus.rdata;
                3'd2: I_rd_data[ 63: 32] <= I_bus.rdata;
                3'd3: I_rd_data[ 95: 64] <= I_bus.rdata;
                3'd4: I_rd_data[127: 96] <= I_bus.rdata;
                default: I_rd_data <= I_rd_data; // Warning, it is not 0
            endcase
        end
    end 
    // address
    always_comb begin
        if (I_rd_state == I_RD_RECREQ) begin
            I_bus.arvalid = 1'b1;
            I_bus.araddr = I_rd_addr;
        end else begin
            I_bus.arvalid = 1'b0;
            I_bus.araddr = '0;
        end
    end
    // rready
    always_comb begin
        if ((I_rd_state == I_RD_WAIT1 ||
             I_rd_state == I_RD_WAIT2 ||
             I_rd_state == I_RD_WAIT3 ||
             I_rd_state == I_RD_WAIT4)
            && I_bus.rvalid == 1'b1) begin
            I_bus.rready = 1'b1;
        end
        else begin
            I_bus.rready = 1'b0;
        end
    end // According to AXI manual, it is still okay to determine ready by valid
    // AXI Interface
    always_comb begin
        if (I_rd_state == I_RD_FINISH) begin
            ICache_axi_interface.ret_valid = 1'b1;
            ICache_axi_interface.ret_data = I_rd_data;
        end else begin
            ICache_axi_interface.ret_valid = 1'b0;
            ICache_axi_interface.ret_data = '0;
        end
    end // This state is specially for processing returning values.


/*----------------------------------DBUS------------------------------------*/
    assign D_bus.arid    = 4'b0001;  // data fetch id 1
    assign D_bus.arlen   = 4'b0011;
    assign D_bus.arsize  = 3'b010;
    assign D_bus.arburst = 2'b01;
    assign D_bus.arlock  = '0;
    assign D_bus.arcache = '1;
    assign D_bus.arprot  = '0;

    assign D_bus.awid    = 4'b0010;  // data fetch id 1
    assign D_bus.awlen   = 4'b0011;  // same as read
    assign D_bus.awsize  = 3'b010;
    assign D_bus.awburst = 2'b01;
    assign D_bus.awlock  = '0;
    assign D_bus.awcache = '1;
    assign D_bus.awprot  = '0;

    assign D_bus.wid     = 4'b0011;
    assign D_bus.wstrb   = 4'b1111; // all of the 4 bytes enabled

    assign D_bus.bready  = 1'b1;

    assign DCache_axi_interface.rd_rdy  = (D_rd_state == D_RD_EMPTY) ? 1'b1 : 1'b0;
    assign DCache_axi_interface.wr_rdy  = (D_wr_state == D_WR_EMPTY) ? 1'b1 : 1'b0;

    always_ff @(posedge clk) begin
        if (rst == `RstEnable) begin
            D_rd_addr <= '0;
        end 
        else if ( DCache_axi_interface.rd_req == 1'b1 && D_rd_state == D_RD_EMPTY ) begin
            D_rd_addr <= DCache_axi_interface.rd_addr;
        end
    end
    always_ff @(posedge clk) begin
        if (rst == `RstEnable) begin
            D_wr_addr <= '0;
            D_wr_data <= '0;
        end 
        else if ( DCache_axi_interface.wr_req == 1'b1 && D_wr_state == D_WR_EMPTY ) begin
            D_wr_addr <= DCache_axi_interface.wr_addr;
            D_wr_data <= DCache_axi_interface.wr_data;
        end
    end
    // FSM Sequential logic
    always_ff @( posedge clk ) begin
        if (rst == `RstEnable) begin
            D_rd_state <= D_RD_EMPTY;
        end else begin
            D_rd_state <= D_rd_next_state;
        end
    end
    always_ff @( posedge clk ) begin
        if (rst == `RstEnable) begin
            D_wr_state <= D_WR_EMPTY;
        end else begin
            D_wr_state <= D_wr_next_state;
        end
    end
    /*=============================D Read======================================*/
        // State transition & Data transfer select
        always_comb begin
            unique case (D_rd_state)
                D_RD_EMPTY: begin
                    D_rd_data_selector = '0;
                    if (DCache_axi_interface.rd_req == 1'b1) begin
                        D_rd_next_state = D_RD_RECREQ;
                    end else begin
                        D_rd_next_state = D_RD_EMPTY;
                    end
                end
                D_RD_RECREQ: begin
                    D_rd_data_selector = '0;
                    if (D_bus.arvalid == 1'b1 && D_bus.arready == 1'b1) begin
                        D_rd_next_state = D_RD_WAIT1;
                    end else begin
                        D_rd_next_state = D_RD_RECREQ;
                    end
                end
                D_RD_WAIT1: begin
                    if (D_bus.rvalid == 1'b1 && D_bus.rready == 1'b1) begin
                        D_rd_next_state = D_RD_WAIT2;
                        D_rd_data_selector = 3'd1;
                    end else begin
                        D_rd_next_state = D_RD_WAIT1;
                        D_rd_data_selector = 3'd0;
                    end
                end
                D_RD_WAIT2: begin
                    if (D_bus.rvalid == 1'b1 && D_bus.rready == 1'b1) begin
                        D_rd_next_state = D_RD_WAIT3;
                        D_rd_data_selector = 3'd2;
                    end else begin
                        D_rd_next_state = D_RD_WAIT2;
                        D_rd_data_selector = 3'd0;
                    end
                end
                D_RD_WAIT3: begin
                    if (D_bus.rvalid == 1'b1 && D_bus.rready == 1'b1) begin
                        D_rd_next_state = D_RD_WAIT4;
                        D_rd_data_selector = 3'd3;
                    end else begin
                        D_rd_next_state = D_RD_WAIT3;
                        D_rd_data_selector = 3'd0;
                    end
                end
                D_RD_WAIT4: begin
                    if (D_bus.rvalid == 1'b1 && D_bus.rready && D_bus.rlast == 1'b1) begin
                        D_rd_next_state = D_RD_FINISH;
                        D_rd_data_selector = 3'd4;
                    end else begin
                        D_rd_next_state = D_RD_WAIT4;
                        D_rd_data_selector = 3'd0;
                    end
                end
                D_RD_FINISH: begin
                    D_rd_next_state = D_RD_EMPTY;
                    D_rd_data_selector = '0;
                end
                default: begin
                    D_rd_next_state = D_RD_EMPTY;
                    D_rd_data_selector = '0;
                end
            endcase
        end
        // fill data into cache by clk
        always_ff @ (posedge clk) begin
            if (rst == `RstEnable) begin
                D_rd_data <= '0;
            end else begin
                unique case (D_rd_data_selector)
                    3'd1: D_rd_data[ 31:  0] <= D_bus.rdata;
                    3'd2: D_rd_data[ 63: 32] <= D_bus.rdata;
                    3'd3: D_rd_data[ 95: 64] <= D_bus.rdata;
                    3'd4: D_rd_data[127: 96] <= D_bus.rdata;
                    default: D_rd_data <= D_rd_data;
                endcase
            end
        end
        // address
        always_comb begin
            if (D_rd_state == D_RD_RECREQ) begin
                D_bus.arvalid = 1'b1;
                D_bus.araddr = D_rd_addr;
            end else begin
                D_bus.arvalid = 1'b0;
                D_bus.araddr = '0;
            end
        end
        // rready
        always_comb begin
            if ((D_rd_state == D_RD_WAIT1 ||
                D_rd_state == D_RD_WAIT2 ||
                D_rd_state == D_RD_WAIT3 ||
                D_rd_state == D_RD_WAIT4)
                && D_bus.rvalid == 1'b1) begin
                D_bus.rready = 1'b1;
            end
            else begin
                D_bus.rready = 1'b0;
            end
        end
        // AXI Interface
        always_comb begin
            if (D_rd_state == D_RD_FINISH) begin
                DCache_axi_interface.ret_valid = 1'b1;
                DCache_axi_interface.ret_data = D_rd_data;
            end else begin
                DCache_axi_interface.ret_valid = 1'b0;
                DCache_axi_interface.ret_data = '0;
            end
        end

    /*=============================D Write======================================*/
        // State Machine Logic & data transfer logic
        always_comb begin
            unique case (D_wr_state)
                D_WR_EMPTY: begin
                    D_bus.wdata = '0;
                    if (DCache_axi_interface.wr_req == 1'b1) begin
                        D_wr_next_state = D_WR_RECREQ;
                    end else begin
                        D_wr_next_state = D_WR_EMPTY;
                    end
                end
                D_WR_RECREQ: begin
                    D_bus.wdata = '0;
                    if (D_bus.awready == 1'b1) begin
                        D_wr_next_state = D_WR_WAIT1;
                    end else begin
                        D_wr_next_state = D_WR_RECREQ;
                    end
                end
                D_WR_WAIT1: begin
                    D_bus.wdata = D_wr_data[31:0];
                    if (D_bus.wready == 1'b1) begin
                        D_wr_next_state = D_WR_WAIT2;
                    end else begin
                        D_wr_next_state = D_WR_WAIT1;
                    end
                end
                D_WR_WAIT2: begin
                    D_bus.wdata = D_wr_data[63:32];
                    if (D_bus.wready == 1'b1) begin
                        D_wr_next_state = D_WR_WAIT3;
                    end else begin
                        D_wr_next_state = D_WR_WAIT2;
                    end
                end
                D_WR_WAIT3: begin
                    D_bus.wdata = D_wr_data[95:64];
                    if (D_bus.wready == 1'b1) begin
                        D_wr_next_state = D_WR_WAIT4;
                    end else begin
                        D_wr_next_state = D_WR_WAIT3;
                    end
                end
                D_WR_WAIT4: begin
                    D_bus.wdata = D_wr_data[127:96];
                    if (D_bus.wready == 1'b1) begin
                        D_wr_next_state = D_WR_S;
                    end else begin
                        D_wr_next_state = D_WR_WAIT4;
                    end
                end
                D_WR_S: begin
                    D_bus.wdata = '0;
                    if (D_bus.bvalid == 1'b1) begin
                        D_wr_next_state = D_WR_FINISH;
                    end else begin
                        D_wr_next_state = D_WR_S;
                    end
                end
                D_WR_FINISH: begin
                    D_bus.wdata = '0;
                    D_wr_next_state = D_WR_EMPTY;
                end
                default: begin
                    D_bus.wdata = '0;
                    D_wr_next_state = D_WR_EMPTY;
                end
            endcase
        end
        // wlast
        always_comb begin
            if (D_wr_state == D_WR_WAIT4) begin
                D_bus.wlast = 1'b1;
            end else begin
                D_bus.wlast = 1'b0;
            end
        end
        // address
        always_comb begin
            if (D_wr_state == D_WR_RECREQ) begin
                D_bus.awaddr  = D_wr_addr;
                D_bus.awvalid = 1'b1;
            end else begin
                D_bus.awaddr  = '0;
                D_bus.awvalid = '0;
            end
        end
        // wvalid
        always_comb begin
            if (D_wr_state == D_WR_WAIT1 || 
                D_wr_state == D_WR_WAIT2 ||
                D_wr_state == D_WR_WAIT3 ||
                D_wr_state == D_WR_WAIT4) begin
                D_bus.wvalid = 1'b1;
            end else begin
                D_bus.wvalid = '0;            
            end
        end
        // AXI Interface
        always_comb begin
            if (D_wr_state == D_WR_FINISH) begin
                DCache_axi_interface.wr_valid = 1'b1;
            end else begin
                DCache_axi_interface.wr_valid = 1'b0;
            end
        end
/*----------------------------------UIBUS------------------------------------*/
    assign UI_bus.arid      = 4'b0100;
    assign UI_bus.arlen     = '0;
    assign UI_bus.arsize    = 3'b010; // well, no fucking instruction is byte or half
    assign UI_bus.arburst   = 2'b01;
    assign UI_bus.awlock    = '0;
    assign UI_bus.awcache   = '0;
    assign UI_bus.awprot    = '0;
    
    assign UI_bus.awid      = '0;
    assign UI_bus.awaddr    = '0;
    assign UI_bus.awlen     = '0;
    assign UI_bus.awsize    = '0;
    assign UI_bus.awburst   = '0;
    assign UI_bus.awlock    = '0;
    assign UI_bus.awcache   = '0;
    assign UI_bus.awprot    = '0;
    assign UI_bus.awvalid   = '0;
    
    assign UI_bus.wid       = '0;
    assign UI_bus.wdata     = '0;
    assign UI_bus.wstrb     = '0;
    assign UI_bus.wlast     = '0;
    assign UI_bus.wvalid    = '0;

    assign UI_bus.bready    = '0;

    assign UICache_axi_interface.rd_rdy  = (UI_rd_state == U_RD_EMPTY) ? 1'b1 : 1'b0;
    // Buffer read
    always_ff @(posedge clk) begin
        if (rst == `RstEnable) begin
            UI_rd_addr <= '0;
        end 
        else if ( UICache_axi_interface.rd_req == 1'b1 && UI_rd_state == U_RD_EMPTY ) begin
            UI_rd_addr <= UICache_axi_interface.rd_addr;
        end
    end

    always_ff @( posedge clk ) begin
        if (rst == `RstEnable) begin
            UI_rd_state <= U_RD_EMPTY;
        end else begin
            UI_rd_state <= UI_rd_next_state;
        end
    end
    // State transition
    always_comb begin
        unique case(UI_rd_state)
            U_RD_EMPTY: begin
                if (UICache_axi_interface.rd_req == 1'b1) begin
                    UI_rd_next_state = U_RD_RECREQ;
                end else begin
                    UI_rd_next_state = U_RD_EMPTY;
                end
            end
            U_RD_RECREQ: begin
                if (UI_bus.arready == 1'b1) begin
                    UI_rd_next_state = U_RD_WAIT;
                end else begin
                    UI_rd_next_state = U_RD_RECREQ;
                end
            end
            U_RD_WAIT: begin
                if (UI_bus.rready == 1'b1 && UI_bus.rlast == 1'b1) begin
                    UI_rd_next_state = U_RD_FINISH;
                end else begin
                    UI_rd_next_state = U_RD_WAIT;
                end
            end
            U_RD_FINISH: begin
                UI_rd_next_state = U_RD_EMPTY;
            end
            default: begin
                UI_rd_next_state = U_RD_EMPTY;
            end
        endcase
    end
    // fill data into cache by clk
    always_ff @(posedge clk) begin
        if (rst == `RstEnable) begin
            UI_rd_data <= '0;
        end else begin
            UI_rd_data <= UI_bus.rdata;
        end
    end
    // address
    always_comb begin
        if (UI_rd_state == U_RD_RECREQ) begin
            UI_bus.araddr = UI_rd_addr;
            UI_bus.arvalid = 1'b1;
        end else begin
            UI_bus.araddr = '0;
            UI_bus.arvalid = 1'b0;
        end
    end
    // rready
    always_comb begin
        if (UI_rd_state == U_RD_WAIT && UI_bus.rvalid == 1'b1) begin
            UI_bus.rready = 1'b1;
        end else begin
            UI_bus.rready = 1'b0;
        end
    end
    // AXI Interface
    always_comb begin
        if (UI_rd_state == U_RD_FINISH) begin
            UICache_axi_interface.ret_valid = 1'b1;
            UICache_axi_interface.ret_data = UI_rd_data;
        end else begin
            UICache_axi_interface.ret_valid = 1'b0;
            UICache_axi_interface.ret_data = '0;
        end
    end

/*----------------------------------UDBUS------------------------------------*/
    assign UD_bus.arid      = 4'b0101;
    assign UD_bus.arlen     = 4'b0000;
    assign UD_bus.arsize    =   (UDCache_axi_interface.LoadType[2]==1'b1) ? 3'b010 ://word
                                (UDCache_axi_interface.LoadType[1]==1'b1) ? 3'b001 ://half
                                                                            3'b000; //byte
    // TODO: Not Sure whether a buffer is needed
    assign UD_bus.arburst = 2'b01;
    assign UD_bus.arlock  = '0;
    assign UD_bus.arcache = '0;
    assign UD_bus.arprot  = '0;

    assign UD_bus.awid    = 4'b0110;
    assign UD_bus.awlen   = 4'b0000;  // only once
    assign UD_bus.awsize  = 3'b010;
    assign UD_bus.awburst = 2'b01;
    assign UD_bus.awlock  = '0;
    assign UD_bus.awcache = '0;
    assign UD_bus.awprot  = '0;

    assign UD_bus.wid     = 4'b0001;
    assign UD_bus.wstrb   =  UD_wr_wstrb;

    assign UD_bus.bready  = 1'b1;

    assign UDCache_axi_interface.rd_rdy = (UD_rd_state == U_RD_EMPTY) ? 1'b1 : 1'b0;
    assign UDCache_axi_interface.wr_rdy = (UD_wr_state == U_WR_EMPTY) ? 1'b1 : 1'b0;
    
    always_ff @(posedge clk) begin
        if (rst == `RstEnable) begin
            UD_rd_addr <= '0;
        end 
        else if ( UDCache_axi_interface.rd_req == 1'b1 && UD_rd_state == U_RD_EMPTY ) begin
            UD_rd_addr <= UDCache_axi_interface.rd_addr;
        end
    end
    always_ff @(posedge clk) begin
        if (rst == `RstEnable) begin
            UD_wr_addr <= '0;
            UD_wr_data <= '0;
            UD_wr_wstrb <= '0;
        end 
        else if ( UDCache_axi_interface.wr_req == 1'b1 && UD_wr_state == U_WR_EMPTY ) begin
            UD_wr_addr <= UDCache_axi_interface.wr_addr;
            UD_wr_data <= UDCache_axi_interface.wr_data;
            UD_wr_wstrb <= UDCache_axi_interface.wr_wstrb;
        end
    end

    /*=================================UD read===============================*/
        always_ff @( posedge clk ) begin
            if (rst == `RstEnable) begin
                UD_rd_state <= U_RD_EMPTY;
            end else begin
                UD_rd_state <= UD_rd_next_state;
            end
        end
        // State transition
        always_comb begin
            unique case(UD_rd_state)
                U_RD_EMPTY: begin
                    if (UDCache_axi_interface.rd_req == 1'b1) begin
                        UD_rd_next_state = U_RD_RECREQ;
                    end else begin
                        UD_rd_next_state = U_RD_EMPTY;
                    end
                end
                U_RD_RECREQ: begin
                    if (UD_bus.arready == 1'b1) begin
                        UD_rd_next_state = U_RD_WAIT;
                    end else begin
                        UD_rd_next_state = U_RD_RECREQ;
                    end
                end
                U_RD_WAIT: begin
                    if (UD_bus.rready == 1'b1 && UD_bus.rlast == 1'b1) begin
                        UD_rd_next_state = U_RD_FINISH;
                    end else begin
                        UD_rd_next_state = U_RD_WAIT;
                    end
                end
                U_RD_FINISH: begin
                    UD_rd_next_state = U_RD_EMPTY;
                end
                default: begin
                    UD_rd_next_state = U_RD_EMPTY;
                end
            endcase
        end
        // fill data into cache by clk
        always_ff @(posedge clk) begin
            if (rst == `RstEnable) begin
                UD_rd_data <= '0;
            end else begin
                UD_rd_data <= UD_bus.rdata;
            end
        end
        // address
        always_comb begin
            if (UD_rd_state == U_RD_RECREQ) begin
                UD_bus.araddr = UD_rd_addr;
                UD_bus.arvalid = 1'b1;
            end else begin
                UD_bus.araddr = '0;
                UD_bus.arvalid = 1'b0;
            end
        end
        // rready
        always_comb begin
            if (UD_rd_state == U_RD_WAIT && UD_bus.rvalid == 1'b1) begin
                UD_bus.rready = 1'b1;
            end else begin
                UD_bus.rready = 1'b0;
            end
        end
        // AXI Interface
        always_comb begin
            if (UD_rd_state == U_RD_FINISH) begin
                UDCache_axi_interface.ret_valid = 1'b1;
                UDCache_axi_interface.ret_data = UD_rd_data;
            end else begin
                UDCache_axi_interface.ret_valid = 1'b0;
                UDCache_axi_interface.ret_data = '0;
            end
        end
    /*=================================UD Write==============================*/
        always_ff @( posedge clk ) begin
            if (rst == `RstEnable) begin
                UD_wr_state <= U_WR_EMPTY;
            end else begin
                UD_wr_state <= UD_wr_next_state;
            end
        end
        // State Machine Logic & data transfer logic
        always_comb begin
            unique case(UD_wr_state)
                U_WR_EMPTY: begin
                    UD_bus.wdata = '0;
                    if (UDCache_axi_interface.wr_req == 1'b1) begin
                        UD_wr_next_state = U_WR_RECREQ;
                    end else begin
                        UD_wr_next_state = U_WR_EMPTY;
                    end
                end
                U_WR_RECREQ: begin
                    UD_bus.wdata = '0;
                    if (UD_bus.awready == 1'b1) begin
                        UD_wr_next_state = U_WR_WAIT;
                    end else begin
                        UD_wr_next_state = U_WR_RECREQ;
                    end
                end
                U_WR_WAIT: begin
                    if (UD_bus.wready == 1'b1) begin
                        UD_wr_next_state = U_WR_S;
                        UD_bus.wdata = UD_wr_data;
                    end else begin
                        UD_wr_next_state = U_WR_WAIT;
                        UD_bus.wdata = '0;
                    end
                end
                U_WR_S: begin
                    UD_bus.wdata = '0;
                    if (UD_bus.bvalid == 1'b1) begin
                        UD_wr_next_state = U_WR_FINISH;
                    end else begin
                        UD_wr_next_state = U_WR_S;
                    end
                end
                U_WR_FINISH: begin
                    UD_bus.wdata = '0;
                    UD_wr_next_state = U_WR_EMPTY;
                end
                default: begin
                    UD_bus.wdata = '0;
                    UD_wr_next_state = U_WR_EMPTY;
                end
            endcase
        end
        // wlast
        always_comb begin
            if (UD_wr_state == U_WR_WAIT) begin
                UD_bus.wlast = 1'b1;
            end else begin
                UD_bus.wlast = 1'b0;
            end
        end
        // address
        always_comb begin
            if (UD_wr_state == U_WR_RECREQ) begin
                UD_bus.awaddr  = UD_wr_addr;
                UD_bus.awvalid = 1'b1;
            end else begin
                UD_bus.awaddr  = '0;
                UD_bus.awvalid = '0;
            end
        end
        // wvalid
        always_comb begin
            if (UD_wr_state == U_WR_WAIT) begin
                UD_bus.wvalid = 1'b1;
            end else begin
                UD_bus.wvalid = '0;
            end
        end
        // AXI Interface
        always_comb begin
            if (UD_wr_state == U_WR_FINISH) begin
                UDCache_axi_interface.wr_valid = 1'b1;
            end else begin
                UDCache_axi_interface.wr_valid = 1'b0;
            end
        end
// AXI crossbar ip
    axi_crossbar_cache U_AXI_CROSSBAR_CACHE (
        .aclk             ( clk ),
        .aresetn          ( rst ),
        
        .s_axi_arid       ( {I_bus.arid   ,D_bus.arid    ,UD_bus.arid   , UI_bus.arid   } ),
        .s_axi_araddr     ( {I_bus.araddr ,D_bus.araddr  ,UD_bus.araddr , UI_bus.araddr } ),
        .s_axi_arlen      ( {I_bus.arlen  ,D_bus.arlen   ,UD_bus.arlen  , UI_bus.arlen  } ),
        .s_axi_arsize     ( {I_bus.arsize ,D_bus.arsize  ,UD_bus.arsize , UI_bus.arsize } ),
        .s_axi_arburst    ( {I_bus.arburst,D_bus.arburst ,UD_bus.arburst, UI_bus.arburst} ),
        .s_axi_arlock     ( {I_bus.arlock ,D_bus.arlock  ,UD_bus.arlock , UI_bus.arlock } ),
        .s_axi_arcache    ( {I_bus.arcache,D_bus.arcache ,UD_bus.arcache, UI_bus.arcache} ),
        .s_axi_arprot     ( {I_bus.arprot ,D_bus.arprot  ,UD_bus.arprot , UI_bus.arprot } ),
        .s_axi_arqos      ( '0                                         ),
        .s_axi_arvalid    ( {I_bus.arvalid,D_bus.arvalid ,UD_bus.arvalid, UI_bus.arvalid} ),
        .s_axi_arready    ( {I_bus.arready,D_bus.arready ,UD_bus.arready, UI_bus.arready} ),
        .s_axi_rid        ( {I_bus.rid    ,D_bus.rid     ,UD_bus.rid    , UI_bus.rid    } ),
        .s_axi_rdata      ( {I_bus.rdata  ,D_bus.rdata   ,UD_bus.rdata  , UI_bus.rdata  } ),
        .s_axi_rresp      ( {I_bus.rresp  ,D_bus.rresp   ,UD_bus.rresp  , UI_bus.rresp  } ),
        .s_axi_rlast      ( {I_bus.rlast  ,D_bus.rlast   ,UD_bus.rlast  , UI_bus.rlast  } ),
        .s_axi_rvalid     ( {I_bus.rvalid ,D_bus.rvalid  ,UD_bus.rvalid , UI_bus.rvalid } ),
        .s_axi_rready     ( {I_bus.rready ,D_bus.rready  ,UD_bus.rready , UI_bus.rready } ),
        .s_axi_awid       ( {I_bus.awid   ,D_bus.awid    ,UD_bus.awid   , UI_bus.awid   } ),
        .s_axi_awaddr     ( {I_bus.awaddr ,D_bus.awaddr  ,UD_bus.awaddr , UI_bus.awaddr } ),
        .s_axi_awlen      ( {I_bus.awlen  ,D_bus.awlen   ,UD_bus.awlen  , UI_bus.awlen  } ),
        .s_axi_awsize     ( {I_bus.awsize ,D_bus.awsize  ,UD_bus.awsize , UI_bus.awsize } ),
        .s_axi_awburst    ( {I_bus.awburst,D_bus.awburst ,UD_bus.awburst, UI_bus.awburst} ),
        .s_axi_awlock     ( {I_bus.awlock ,D_bus.awlock  ,UD_bus.awlock , UI_bus.awlock } ),
        .s_axi_awcache    ( {I_bus.awcache,D_bus.awcache ,UD_bus.awcache, UI_bus.awcache} ),
        .s_axi_awprot     ( {I_bus.awprot ,D_bus.awprot  ,UD_bus.awprot , UI_bus.awprot } ),
        .s_axi_awqos      ( '0                                         ),
        .s_axi_awvalid    ( {I_bus.awvalid,D_bus.awvalid ,UD_bus.awvalid, UI_bus.awvalid} ),
        .s_axi_awready    ( {I_bus.awready,D_bus.awready ,UD_bus.awready, UI_bus.awready} ),
        .s_axi_wid        ( {I_bus.wid    ,D_bus.wid     ,UD_bus.wid    , UI_bus.wid    } ),
        .s_axi_wdata      ( {I_bus.wdata  ,D_bus.wdata   ,UD_bus.wdata  , UI_bus.wdata  } ),
        .s_axi_wstrb      ( {I_bus.wstrb  ,D_bus.wstrb   ,UD_bus.wstrb  , UI_bus.wstrb  } ),
        .s_axi_wlast      ( {I_bus.wlast  ,D_bus.wlast   ,UD_bus.wlast  , UI_bus.wlast  } ),
        .s_axi_wvalid     ( {I_bus.wvalid ,D_bus.wvalid  ,UD_bus.wvalid , UI_bus.wvalid } ),
        .s_axi_wready     ( {I_bus.wready ,D_bus.wready  ,UD_bus.wready , UI_bus.wready } ),
        .s_axi_bid        ( {I_bus.bid    ,D_bus.bid     ,UD_bus.bid    , UI_bus.bid    } ),
        .s_axi_bresp      ( {I_bus.bresp  ,D_bus.bresp   ,UD_bus.bresp  , UI_bus.bresp  } ),
        .s_axi_bvalid     ( {I_bus.bvalid ,D_bus.bvalid  ,UD_bus.bvalid , UI_bus.bvalid } ),
        .s_axi_bready     ( {I_bus.bready ,D_bus.bready  ,UD_bus.bready , UI_bus.bready } ),
        
        .m_axi_arid       ( m_axi_arid          ),
        .m_axi_araddr     ( m_axi_araddr        ),
        .m_axi_arlen      ( m_axi_arlen         ),
        .m_axi_arsize     ( m_axi_arsize        ),
        .m_axi_arburst    ( m_axi_arburst       ),
        .m_axi_arlock     ( m_axi_arlock        ),
        .m_axi_arcache    ( m_axi_arcache       ),
        .m_axi_arprot     ( m_axi_arprot        ),
        .m_axi_arqos      (                     ),
        .m_axi_arvalid    ( m_axi_arvalid       ),
        .m_axi_arready    ( m_axi_arready       ),
        .m_axi_rid        ( m_axi_rid           ),
        .m_axi_rdata      ( m_axi_rdata         ),
        .m_axi_rresp      ( m_axi_rresp         ),
        .m_axi_rlast      ( m_axi_rlast         ),
        .m_axi_rvalid     ( m_axi_rvalid        ),
        .m_axi_rready     ( m_axi_rready        ),
        .m_axi_awid       ( m_axi_awid          ),
        .m_axi_awaddr     ( m_axi_awaddr        ),
        .m_axi_awlen      ( m_axi_awlen         ),
        .m_axi_awsize     ( m_axi_awsize        ),
        .m_axi_awburst    ( m_axi_awburst       ),
        .m_axi_awlock     ( m_axi_awlock        ),
        .m_axi_awcache    ( m_axi_awcache       ),
        .m_axi_awprot     ( m_axi_awprot        ),
        .m_axi_awqos      (                     ),
        .m_axi_awvalid    ( m_axi_awvalid       ),
        .m_axi_awready    ( m_axi_awready       ),
        .m_axi_wid        ( m_axi_wid           ),
        .m_axi_wdata      ( m_axi_wdata         ),
        .m_axi_wstrb      ( m_axi_wstrb         ),
        .m_axi_wlast      ( m_axi_wlast         ),
        .m_axi_wvalid     ( m_axi_wvalid        ),
        .m_axi_wready     ( m_axi_wready        ),
        .m_axi_bid        ( m_axi_bid           ),
        .m_axi_bresp      ( m_axi_bresp         ),
        .m_axi_bvalid     ( m_axi_bvalid        ),
        .m_axi_bready     ( m_axi_bready        )
    );
endmodule